package com.own.component.store.all.config;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import com.own.component.autoconfig.property.redis.RedisProperties;
import com.own.component.common.util.serializer.CustomZonedDateTimeSerializer;
import com.own.component.store.core.Store;
import com.own.component.store.core.property.StoreProperty;
import com.own.component.store.core.util.LockStoreUtil;
import com.own.component.store.core.util.RateLimiterUtil;
import com.own.component.store.local.DefaultLocalStore;
import com.own.component.store.local.util.LocalLockStoreUtil;
import com.own.component.store.local.util.LocalRateLimiterUtil;
import com.own.component.store.redis.DefaultRedisStore;
import com.own.component.store.redis.util.RedisLockStoreUtil;
import com.own.component.store.redis.util.RedisRateLimiterUtil;
import com.own.constant.ConstantDateFormatPattern;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.codec.JsonJacksonCodec;
import org.redisson.config.Config;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;

import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZonedDateTime;

/**
 * AutoStoreConfig
 *
 * @author chenxueli
 * @date 2022-08-10 14:36:00
 */
@Slf4j
@SpringBootConfiguration
public class AutoStoreConfig {

    @Resource
    public RedisProperties redisProperties;
    @Resource
    public StoreProperty storeProperty;

    private RedissonClient redissonClient;

    public RedissonClient redissonClient() {
        if (redissonClient != null) {
            return redissonClient;
        }
        if (!redisProperties.getEnabled()) {
            return null;
        }
        var config = new Config();
        var c = config.useSingleServer();
        c.setTimeout((int) redisProperties.getTimeout().getSeconds());
        if (StringUtils.isNotBlank(redisProperties.getUsername())) {
            c.setUsername(redisProperties.getUsername());
        }
        if (StringUtils.isNotBlank(redisProperties.getPassword())) {
            c.setPassword(redisProperties.getPassword());
        }
        c.setAddress("redis://" + redisProperties.getHost() + ":" + redisProperties.getPort());
        c.setDatabase(redisProperties.getDatabase());
        var mapper = new ObjectMapper();
        mapper.setConfig(mapper.getDeserializationConfig().withoutFeatures(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES));
        var javaTimeModule = new JavaTimeModule()
                .addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(ConstantDateFormatPattern.Formatter.DATETIME))
                .addDeserializer(LocalDate.class, new LocalDateDeserializer(ConstantDateFormatPattern.Formatter.DATE))
                .addDeserializer(LocalTime.class, new LocalTimeDeserializer(ConstantDateFormatPattern.Formatter.HH_MM_SS))
                .addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(ConstantDateFormatPattern.Formatter.DATETIME))
                .addSerializer(ZonedDateTime.class, new CustomZonedDateTimeSerializer(ConstantDateFormatPattern.Formatter.DATETIME_SSS))
                .addSerializer(LocalDate.class, new LocalDateSerializer(ConstantDateFormatPattern.Formatter.DATE))
                .addSerializer(LocalTime.class, new LocalTimeSerializer(ConstantDateFormatPattern.Formatter.HH_MM_SS));
        mapper.registerModule(javaTimeModule);
        config.setCodec(new JsonJacksonCodec(mapper));
        redissonClient = Redisson.create(config);
        return redissonClient;
    }


    @Bean
    public Store<String> store() {
        return redisProperties.getEnabled()
                ? new DefaultRedisStore<>(redissonClient())
                : new DefaultLocalStore<>(storeProperty);
    }

    @Bean
    public LockStoreUtil lockStoreUtil() {
        return redisProperties.getEnabled()
                ? new LockStoreUtil(new RedisLockStoreUtil(redissonClient()))
                : new LockStoreUtil(new LocalLockStoreUtil());
    }

    @Bean
    public RateLimiterUtil rateLimiterUtil() {
        return redisProperties.getEnabled()
                ? new RedisRateLimiterUtil(redissonClient())
                : new LocalRateLimiterUtil();
    }

}
